﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Framework.Infrastructure.IoC;
using System.Reflection;
using Framework.Infrastructure;
using Framework.Bus;
using Framework.Event;
using Framework.Config;

namespace Framework.BootStrappers
{
    public abstract class BootStrapper : IBootStrapper
    {  
        #region Private Constants
        private const string RegisterMethodName = @"Register";
        #endregion
     
        #region Members
        /// <summary>
        /// Current IoC container
        /// </summary>
        protected IUnityContainer _CurrentContainer;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new instance of this boot strapper
        /// </summary>
        /// <param name="container">Default IoC container</param>
        public BootStrapper()
        {
            _CurrentContainer = IoCFactory.Instance.CurrentContainer;
            if (_CurrentContainer == (IContainer)null)
                throw new ArgumentNullException("serviceFactory");
        }

        #endregion

        #region IBootStrapper Members

        #region Protected Methods
        /// <summary>
        /// Registers the handlers to the message dispatcher based on the given configuration.
        /// </summary>
        /// <param name="dispatcher">The message dispatcher to which the handlers being registered.</param>
        protected virtual void RegisterHandlers(IMessageDispatcher dispatcher)
        {
            dispatcher.ClearRegistration();
            var handlerElements = FrameworkConfigurationReader.Instance.Config.Handlers;
            if (handlerElements != null)
            {
                foreach (HandlerElement handlerElement in handlerElements)
                {
                    try
                    {
                        switch (handlerElement.SourceType)
                        {
                            case HandlerSourceType.Type:
                                Type type = Type.GetType(handlerElement.Source);
                                RegisterHandlerFromType(dispatcher, handlerElement.Kind, type);
                                break;
                            case HandlerSourceType.Assembly:
                                Assembly assembly = Assembly.Load(handlerElement.Source);
                                RegisterHandlerFromAssembly(dispatcher, handlerElement.Kind, assembly);
                                break;
                        }
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
        }
        #endregion

        #region Private Methods
        private void RegisterHandlerFromAssembly(IMessageDispatcher dispatcher, HandlerKind kind, Assembly assembly)
        {
            var exportedTypes = assembly.GetExportedTypes();
            foreach (var type in exportedTypes)
            {
                RegisterHandlerFromType(dispatcher, kind, type);
            }
        }

        private void RegisterHandlerFromType(IMessageDispatcher dispatcher, HandlerKind kind, Type type)
        {
            Type genericTypeDefinition = null;
            Type dispatcherType = dispatcher.GetType();
            MethodInfo registerMethod = dispatcherType.GetMethod(RegisterMethodName, BindingFlags.Public | BindingFlags.Instance);

            switch (kind)
            {
                case HandlerKind.Event:
                    genericTypeDefinition = typeof(IEventHandler<>);
                    if (!type.IsAbstract &&
                        type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition().Equals(genericTypeDefinition)))
                    {
                        var genericInterfaceType = type.GetInterfaces().Where(t => t.IsGenericType && t.GetGenericTypeDefinition().Equals(genericTypeDefinition)).First();
                        var handleeType = genericInterfaceType.GetGenericArguments().First();
                        var genericRegisterMethod = registerMethod.MakeGenericMethod(handleeType);

                        var handler = _CurrentContainer.Resolve(type);
                        genericRegisterMethod.Invoke(dispatcher, new object[] { handler });
                    }
                    break;
                default: return;
            }
        }
        #endregion

        public virtual void Boot()
        {
            var messageDispatcher = _CurrentContainer.Resolve<IMessageDispatcher>();
            RegisterHandlers(messageDispatcher);
        }

        static readonly string UnityRegisterAssemblies = "UnityRegisterAssemblies";
        protected List<Assembly> GetRegisterAssemblies()
        {
            List<Assembly> assemblies = new List<Assembly>();
            var assembliesNames = Utility.GetAppConfig(UnityRegisterAssemblies);
            var assemblyNameList = assembliesNames.ToJsonObject<List<string>>();
            if (assemblyNameList != null)
            {
                foreach (var assemblyName in assemblyNameList)
                {
                    try
                    {
                        assemblies.Add(Assembly.Load(assemblyName));
                    }
                    catch (System.Exception)
                    {
                    }
                }
            }
            return assemblies;
        }
        #endregion
    }
}
